#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Predesys - Predesys Bluetooth Proxy
# Version: 0.1.1
# Author: Jose A. Jimenez <jajimc@gmail.com>
# License: GPL 3.0 (see "license" file)

import sys
sys.dont_write_bytecode = True
import os
import socket
import threading
import daemon
import configuration
import bluetooth

# File to save process ID
DAEMON_PID_FILE = "/tmp/predesys-bluetooth-proxy.pid"

# File to save exception messages
DAEMON_ERROR_FILE = "/tmp/predesys-bluetooth-proxy.err"

# Configuration file path to load
CONFIG_FILE_1 = "configuration.xml"
CONFIG_FILE_2 = "/etc/predesys-bluetooth-proxy/configuration.xml"

class BluetoothProxy(daemon.Daemon):
    """Predesys Bluetooth Proxy daemon.
    
    It does all the work of the proxy. 
    """
    def __init__(self):
        self.__load_configuration()
        daemon.Daemon.__init__(self, pidfile=DAEMON_PID_FILE, stderr=DAEMON_ERROR_FILE)

    def __load_configuration(self):
        """Loads configuration from CONFIG_FILE_1 file or CONFIG_FILE_2 file.
        
        If an error occurs, it sets the default configuration.
        """
        self.configuration = configuration.BluetoothProxyConfiguration()
        
        if os.path.isfile(CONFIG_FILE_1):
            try:
                self.configuration.load_from_file(CONFIG_FILE_1)
            except:
                if os.path.isfile(CONFIG_FILE_2):
                    try:
                        self.configuration.load_from_file(CONFIG_FILE_2)
                    except:
                        self.configuration.set_default_values()
                else:
                    self.configuration.set_default_values()
        elif os.path.isfile(CONFIG_FILE_2):
            try:
                self.configuration.load_from_file(CONFIG_FILE_2)
            except:
                self.configuration.set_default_values()
        else:
            self.configuration.set_default_values()

    def run(self):
        """Does the daemon work.
        
        It keeps listening to requests from bluetooth clients.
        """
        btServerSocket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
        btServerSocket.bind(("", self.configuration.get_bluetooth_channel()))
        btServerSocket.listen(self.configuration.get_station_max_clients())      
        while(True):
            try:
                btClientSocket, btClientInfo = btServerSocket.accept()
                t = ClientHandlerThread(btClientSocket, btClientInfo, self.configuration)
                t.start()
            except:
                # ToDo: Register error
                pass  
        btServerSocket.close()
                
class ClientHandlerThread(threading.Thread):
    """Thread class for handle every bluetooth client connection."""
    def __init__(self, btClientSocket, btClientInfo, configuration):
        """Class constructor.
        
        Keyword arguments:
        btClientSocket -- Client bluetooth socket (bluetooth.BluetoothSocket)
        btClientInfo -- A tuple with information (address and channel) about the bluetooth client ((string, int))
        configuration -- Proxy configuration (BluetoothProxyConfiguration)
        """
        self.btClientSocket = btClientSocket
        self.btClientInfo = btClientInfo
        self.configuration = configuration
        threading.Thread.__init__(self)
        
    def run(self):
        """Does the daemon work."""
        try:
            self.__handle_client(self.btClientSocket, self.btClientInfo)
        except:
            # ToDo: Register error
            pass
        
    def __handle_client(self, btClientSocket, btClientInfo):
        """Handle a client connection.
        
        Keyword arguments:
        btClientSocket -- Client bluetooth socket (bluetooth.BluetoothSocket)
        btClientInfo -- A tuple with information (address and channel) about the bluetooth client ((string, int))
        """
        # Receive HTTP request from client (bluetooth device) through bluetooth
        request = self.__get_client_request(btClientSocket)

        # Send HTTP request to Predesys Web Service and receive its response through TCP/IP
        response = self.__send_request_toserver(request)
        
        # Send HTTP request response to client through bluetooth
        self.__send_response_toclient(btClientSocket, response)
        
        btClientSocket.close()
      
    def __get_client_request(self, btClientSocket):
        """Gets request from client through bluetooth.
        
        Keyword arguments:
        btClientSocket -- Client bluetooth socket (bluetooth.BluetoothSocket)
        
        It returns request data (string).
        """
        request = ""
        bufferSize = self.configuration.get_bluetooth_buffer_size()
        requestLength = len(request)
        while(True):
            data = btClientSocket.recv(bufferSize)
            request += data
            if request[requestLength-5:] == "<end>": break  
        return request[:len(request)-5]
    
    def __send_request_toserver(self, request):
        """Sends data received from client to Predesys server (a web server) through TCP/IP.
        
        Keyword arguments:
        request -- HTTP request to send to Predesys server (string)
        
        It returns Predesys server response (HTTP response) (string).
        """
        host = self.configuration.get_service_host()
        port = self.configuration.get_service_port()
        bufferSize = self.configuration.get_tcpip_buffer_size()
        
        tcpipClientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        tcpipClientSocket.connect((host, port))
        
        # Send request to Predesys server
        requestLength = len(request)
        sentBytes = 0
        while(sentBytes < requestLength):
            sentBytes += tcpipClientSocket.send(request[sentBytes:sentBytes+bufferSize])
        
        # Receive response from Predesys server
        response = ""            
        while(True):
            data = tcpipClientSocket.recv(bufferSize)
            if not data: break
            response += data
        
        tcpipClientSocket.close()
        return response
        
    def __send_response_toclient(self, btClientSocket, response):
        """Sends Predesys server response (HTTP response) to client through bluetooth.
        
        Keyword arguments:
        btClientSocket -- Client bluetooth socket (bluetooth.BluetoothSocket)
        response -- HTTP response to send (string)
        """
        response2 = response + "<end>"
        responseLength = len(response2)
        sentBytes = 0
        while(sentBytes < responseLength):
            sentBytes += btClientSocket.send(response2[sentBytes:])
            
if __name__ == "__main__":
    if len(sys.argv) == 2:
        if sys.argv[1] == "start":
            daemon = BluetoothProxy()
            daemon.start()
        elif sys.argv[1] == "restart":
            daemon = BluetoothProxy()
            daemon.restart()
        elif sys.argv[1] == "stop":
            daemon = BluetoothProxy()
            daemon.stop()
        else:
            print "Unknown command"
            sys.exit(2)
        sys.exit(0)
    else:
        print "Usage: %s start|stop|restart" % sys.argv[0]
        sys.exit(2)
